
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Crux.Core.StackExchangeRedis;
/// <inheritdoc/>
public class RedisConnectionHelm(
    IOptions<ConfigurationOptions> option,
    ILogger<RedisConnectionHelm> logger,
    IOptionsMonitor<ConfigurationOptions> monitor)
    : IDisposableObservable,
    IRedisConnectionHelm
{
    #region private fields
    private int _location = 0;
    private readonly ILogger<RedisConnectionHelm> logger = logger ?? throw new ArgumentNullException(nameof(logger));
    private readonly ConfigurationOptions configurationOptions = option.Value;
    private IConnectionMultiplexer? _multiplexer;
    #endregion  private fields

    #region implements IDisposableObservable
    /// <inheritdoc/>
    public bool IsDisposed { get; private set; }
    /// <inheritdoc/>
    public void Dispose()
    {
        if (IsDisposed) return;

        IsDisposed = true;

        if (_multiplexer is not null)
        {
            using var _ = _multiplexer;
        }

        GC.SuppressFinalize(this);
    }
    #endregion implements IDisposableObservable

    #region implements IRedisConnectionHelm
    /// <inheritdoc/>
    public IConnectionMultiplexer TryGetConnectionMultiplexer()
    {
        while (true)
        {
            if (_multiplexer is not null && _multiplexer.IsConnected)
            {
                return _multiplexer;
            }

            if (Interlocked.CompareExchange(ref _location, 1, 0) != 0)
            {
                Thread.Sleep(1);
                continue;
            }

            try
            {
                if (_multiplexer is not null)
                {
                    if (_multiplexer.IsConnected)
                    {
                        return _multiplexer;
                    }

                    using var _ = _multiplexer;
                }

                _multiplexer = ConnectionMultiplexer.Connect(configurationOptions);
                _multiplexer.ConnectionFailed += (s, e) =>
                {
                    logger?.LogError(e.Exception, e.Exception?.Message);
                };

                _multiplexer.ErrorMessage += (s, e) =>
                {
                    logger?.LogError(e.Message);
                };

                _multiplexer.InternalError += (s, e) =>
                {
                    logger?.LogError(e.Exception, e.Exception?.Message);
                };

                IDisposable? hook = default;

                hook = monitor.OnChange((option) =>
                {
                    using var _ = _multiplexer;

                    _multiplexer = default;

                    using var __ = hook;
                });

                return _multiplexer;
            }
            finally
            {
                Interlocked.Exchange(ref _location, 0);
            }
        }
    }
    #endregion implements IRedisConnectionHelm
}